/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/

#include "ImageDisplay.hpp"
#include <GL/glut.h>
#include <iostream>
#include <math.h>

//function for drawing text
void drawText(float x, float y, void *font, const char *string)
{
  const char *c;
  glRasterPos2f(x,y);
  for (c=string; *c != '\0'; c++)
    {
      glutBitmapCharacter(font, *c);
    }
}

const int CImageDisplay::infoAreaHeight = 12;

CImageDisplay::CImageDisplay()
  :texture(0),
   imageRatio(0),
   imageWidth(0),
   imageHeight(0),
   texWidth(0),
   texHeight(0),
   imageContainerIndex(0),
   iFlipImage(false),
   iStreamCount(0),
   texCoordX(0),
   texCoordY(0),
   vertexX(1),
   vertexY(1),
   sizeChanged(false),
   RGBData(NULL),
   connected(false),
   opened(false),
   displayInfo(false),
   displayWarnings(false),
   imageClient(NULL)
{
  imageMutex=ownMutex_Init();
}

CImageDisplay::~CImageDisplay()
{
  if (RGBData)
    delete[] RGBData;
  
  if (texture)
    glDeleteTextures(1,&texture);
}

void CImageDisplay::DecreaseImageContainerIndex()
{
  imageContainerIndex--;
  if(imageContainerIndex < 0)
    imageContainerIndex = 0;
}   
void CImageDisplay::IncreaseImageContainerIndex()
{
  imageContainerIndex++;
  if(imageContainerIndex >= iStreamCount)
    imageContainerIndex = iStreamCount - 1;
}   

//*****************************************************************************
//*****************************************************************************

 /** Toggles flipping of the image (up/down)
   *
   */
void CImageDisplay::ToggleFlip()
{
  //dPrint(0, "Toggling flip!");

  if(iFlipImage)
    {
      iFlipImage = false;

    } else 
    {
      iFlipImage = true;
    }


}

//*****************************************************************************
//*****************************************************************************

void CImageDisplay::Draw(int imageIndex)
{
  glPushAttrib(GL_ALL_ATTRIB_BITS);
  
  glEnable(GL_TEXTURE_2D);
  glDisable(GL_LIGHTING);
  
  bool slowFps=false;
  
  if (!connected)
    {
      //display error
      glColor3f(0.1,0.1,0.1);
      glBegin(GL_QUADS);
      glVertex2f(0,0);
      
      glVertex2f(vertexX,0);
      
      glVertex2f(vertexX,vertexY);
      
      glVertex2f(0,vertexY);
      glEnd();
      
      glColor3f(0.8,0.8,0.8);
      glBegin(GL_LINES);
      glVertex2f(0.2,0.2);
      glVertex2f(0.8,0.8);
        
      glVertex2f(0.2,0.8);
      glVertex2f(0.8,0.2);
      glEnd();
    
      //TODO:display text that we are not connected
    } else {
  
      if (opened)
        {
          if (sizeChanged && RGBData)
            {
              sizeChanged=false;
        
              //create the texture
              if (texture)
                {
                  glDeleteTextures(1,&texture);
                }
        
              glGenTextures(1, &texture);
              glBindTexture(GL_TEXTURE_2D, texture);
              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
                              GL_NEAREST);
              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                              GL_NEAREST);
              glPixelStorei(GL_PACK_ALIGNMENT,1);
              glPixelStorei(GL_UNPACK_ALIGNMENT,1);
              //create a texture big enough to hold camera image
              //find closest powers of 2
              texWidth=(int)pow(2,ceil(log2(imageWidth)));
              texHeight=(int)pow(2,ceil(log2(imageHeight)));
              //calculate images coordinates in texture
              texCoordX=1.0*imageWidth/texWidth;
              texCoordY=1.0*imageHeight/texHeight;
        
              if (imageWidth>=imageHeight)
                {
                  vertexX=1.0;
                  vertexY=vertexX/imageRatio;
                } else {
                  vertexY=1.0;
                  vertexX=imageRatio*vertexY;
                }
        
              //dPrint(1,"vx:%d vy:%d",vertexX, vertexY);
        
              unsigned char *temp=new unsigned char[texWidth*texHeight*3];
              memset(temp,128,texWidth*texHeight*3);
  
              //        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGB,
              //                    GL_UNSIGNED_BYTE, temp);
  
              glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texWidth, texHeight, 0, GL_RGB,
                           GL_UNSIGNED_BYTE, temp);
        
              delete[] temp;
        
            }

          glBindTexture(GL_TEXTURE_2D, texture);
      
          ownMutex_Lock(imageMutex);
      
          gim::time diff(true);
          diff-=lastImageTime;
          if (diff.getTimeMs()>3000)
            slowFps=true;
      
          //draw the image on texture
          if (RGBData)
            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, imageWidth, imageHeight, GL_RGB,
                            GL_UNSIGNED_BYTE, RGBData);
      
          ownMutex_Unlock(imageMutex);
      
          glColor3f(1,1,1);
      

          if(iFlipImage)
            {
              //draw quad with image as texture
              // Flipped

              glBegin(GL_QUADS);
              glTexCoord2d(texCoordX, 0);
              glVertex2f(0,0);
              
              glTexCoord2d(0, 0);
              glVertex2f(vertexX,0);
              
              glTexCoord2d(0,texCoordY);
              glVertex2f(vertexX,vertexY);
              
              glTexCoord2d(texCoordX,texCoordY);
              glVertex2f(0,vertexY);
              glEnd();

            } else
            {


              //draw quad with image as texture
              glBegin(GL_QUADS);
              glTexCoord2d(0,texCoordY);
              glVertex2f(0,0);
              
              glTexCoord2d(texCoordX,texCoordY);
              glVertex2f(vertexX,0);
              
              glTexCoord2d(texCoordX,0);
              glVertex2f(vertexX,vertexY);
              
              glTexCoord2d(0,0);
              glVertex2f(0,vertexY);
              glEnd();
          
            }
      
          glDisable(GL_TEXTURE_2D);

          

      
          //display possible warnings or errors
          if (displayWarnings)
            {
              if (slowFps)
                {
                  //draw a red quad as pics frame
                  glColor3f(1,0,0);
                  glBegin(GL_LINE_LOOP);
                  glVertex2f(0+0.01,0+0.01);
      
                  glVertex2f(vertexX-0.01,0+0.01);
      
                  glVertex2f(vertexX-0.01,vertexY-0.01);
      
                  glVertex2f(0+0.01,vertexY-0.01);
                  glEnd();
                }
            }
      
        } else {
          //display error
          glColor3f(0.1,0.1,0.1);
          glBegin(GL_QUADS);
          glVertex2f(0,0);
      
          glVertex2f(vertexX,0);
      
          glVertex2f(vertexX,vertexY);
      
          glVertex2f(0,vertexY);
          glEnd();
      
          glColor3f(1,0,0);
          glBegin(GL_LINES);
          glVertex2f(0.2,0.2);
          glVertex2f(0.8,0.8);
        
          glVertex2f(0.2,0.8);
          glVertex2f(0.8,0.2);
          glEnd();
        }
    } //if connected
  glPopAttrib();
}

void CImageDisplay::DisplayInfo(bool info)
{
  displayInfo=info;
}

void CImageDisplay::DisplayWarnings(bool warnings)
{
  displayWarnings=warnings;
}

int CImageDisplay::GetWidth()
{
  return imageWidth;
}

int CImageDisplay::GetHeight()
{
  return imageHeight;
}

bool CImageDisplay::Connect(gimi::GIMI *gimi, int gimnetID, int minor)
{
  connected=true;
  
  imageClient= new MaCI::Image::CImageClient(gimi,minor);
  
  imageClient->SetDefaultTarget(gimnetID);
  
  imageClient->SetImageClientCallback(this);
  
  if (imageClient->Open() == MaCI::KMaCIOK)
    {
      opened=true;
    } else {
      opened=false;
      delete imageClient;
      imageClient=NULL;
    }
  return opened;
}

void Disconnect()
{
  
}

void CImageDisplay::OnImageDataEvent(const MaCI::Image::CImageData &aData, 
                                     const unsigned int aDataSequence, 
                                     MaCI::Image::CImageClient &aImageClient)
{
  MaCI::Image::CImageContainer ic;
  MaCI::Image::TImageDataIterator dataIte = NULL;
  
  //We set the stream count value
  iStreamCount = aData.GetImageCount();

  //We limit the max imageContainerIndex
  if(imageContainerIndex >= (int)aData.GetImageCount())
    imageContainerIndex = aData.GetImageCount() - 1;

  
  //We iterate to the wanted index.
  for(int i = 0; i <= imageContainerIndex; i++)
    {
      if(!aData.GetImage(ic, &dataIte, false));
    }

  int w,h;
  w=ic.GetImageInfoRef().imagewidth;
  h=ic.GetImageInfoRef().imageheight;
    
  if (w!=imageWidth || h!=imageHeight)
    {
      sizeChanged=true;
    }
    
  ownMutex_Lock(imageMutex);
    
  lastImageTime.setToCurrent();
    
  imageWidth=w;
  imageHeight=h;
    
  imageRatio=1.0*imageWidth/imageHeight;
    
  if (ic.ConvertTo(MaCI::Image::KImageDataRGB))
    {
      if (!RGBData || sizeChanged)
        {
          if (RGBData)
            delete[] RGBData;

          RGBData=new unsigned char[ic.GetImageDataSize()];
        }
      
      memcpy(RGBData,ic.GetImageDataPtr(),ic.GetImageDataSize());
      
    } else {
      dPrint(1,"Couldnt convert image data to rgb!");
    }
    
  ownMutex_Unlock(imageMutex);
    
 
}

void CImageDisplay::drawInfo(std::string message)
{
  //draw a black box
  glColor3f(0,0,0);
  glRectf(0, 0, vertexX, infoAreaHeight);
  //...and the message inside it
  glColor3f(1,1,1);
  drawText(0,0,GLUT_BITMAP_TIMES_ROMAN_10, message.c_str());
}


